
package mamayukero.ai.ga;

import java.util.ArrayList;
import java.util.Random;
import mamayukero.problem.environment.Environment;
import mamayukero.problem.environment.component.Machine;
import mamayukero.problem.environment.component.Product;
import mamayukero.problem.environment.component.Vendor;
import mamayukero.ui.ga.UIVisualisasi;

public class GABase {
    public static ArrayList<Individual> oldPopulation, newPopulation;
    public static int generationCounter, maxCounter;
    public static Random randomizer;
    
    //fitness variable
    public static float sumFitness, avgFitness, maxFitness = 0, minFitness;
    public static String maxAllele = "";
  
    //global for genetic algorithm
    public static int MAX_POP = 30;           //population size
    public static int ALLE_LENGTH = 8;  //default length of one allele
    
    //chances
    public static int pCross = 25, pMutation = 10;
    
    //Machine,Product,Vendor
    public static Machine machine;
    public static Product product;
    public static Vendor vendor;
    
    public static boolean isLoop = true;
    
    public ArrayList<Integer> persentase;
    public ArrayList<Float> persenSelect;
    
    public GABase() {
        persentase = new ArrayList<Integer>(Environment.typesOfMachine.size());
        persenSelect = new ArrayList<Float>(MAX_POP);
        countPersentase();
        oldPopulation = new ArrayList<Individual>(MAX_POP);
        newPopulation = new ArrayList<Individual>(MAX_POP);
        randomizer = new Random();
        maxCounter = Environment.numberOfMachine;         //dari class hasil parsing mesin
        generationCounter = (randomizer.nextInt(1000) % maxCounter)+ 1;
    }
    
    public void randomAwal() {
        ALLE_LENGTH = Environment.hourOfWork-1;  //harusnya lamanya waktu kerja pabrik dalam 1 hari - 1 periode
        
        int persen = 0;
        
        UIVisualisasi.butStart.setEnabled(false);
        UIVisualisasi.butStop.setEnabled(false);
        //generate population
        while (newPopulation.size() != MAX_POP) {
            String allele = new String();
            for (int j = 0; j < ALLE_LENGTH; j++) {
                while ((persen = (randomizer.nextInt(1000) % 100)) >= persentase.get(generationCounter-1)) {
                    generationCounter = (randomizer.nextInt(1000) % maxCounter) + 1;
                }
                allele = allele + (char)(generationCounter + 48);
                generationCounter = (randomizer.nextInt(1000) % maxCounter) + 1;
            }
            newPopulation.add(new Individual(allele));
//            if (newPopulation.get(newPopulation.size()-1).getFitness() == 0) {
//                newPopulation.remove(newPopulation.size()-1);
//            }
            if (newPopulation.size() == MAX_POP) {
                UIVisualisasi.textAsli.setText(printS());
                if (allZero()) {
                    for (int i = 0; i < 15; i++) {
                        if (newPopulation.get(i).getFitness() == 0)
                            newPopulation.remove(i);
                    }
                }
            }
            generationCounter = (randomizer.nextInt(1000) % maxCounter) + 1;
        }
        UIVisualisasi.butStart.setEnabled(true);
        UIVisualisasi.butStop.setEnabled(true);
        UIVisualisasi.labelRandom.setText("");
    }
    
    public void Select() {
        update();
        countPersenSelect();
        oldPopulation = new ArrayList<Individual>(newPopulation);
        newPopulation = new ArrayList<Individual>(MAX_POP);
        int i = 0;
        while (i < oldPopulation.size()) {
            if (persenSelect.get(i) > 3.2) {
                newPopulation.add(new Individual(oldPopulation.get(i)));
                i++;
            } else {
                oldPopulation.remove(i);
                persenSelect.remove(i);
            }
        }
        
        int size = newPopulation.size();
        if (newPopulation.size() < MAX_POP) {
            for (int j = 0; j < (MAX_POP - size); j++) {
                generationCounter = randomizer.nextInt(1000) % oldPopulation.size();
                newPopulation.add(new Individual(oldPopulation.get(generationCounter)));
            }
        }
    }
    
    public void CrossOver() {
        update();
        oldPopulation = new ArrayList<Individual>(newPopulation);
        newPopulation = new ArrayList<Individual>(MAX_POP);
        for (int i = 0; i < MAX_POP; i+=2) {
            generationCounter = randomizer.nextInt(100000) % 100;
            if (generationCounter < pCross) {
                String allele11; //individu 1 chromosom 1
                String allele12; //individu 1 chromosom 2
                String allele21; //individu 2 chromosom 1
                String allele22; //individu 2 chromosom 2
                generationCounter = (randomizer.nextInt(1000) % (ALLE_LENGTH - 1)) + 1;
                allele11 = oldPopulation.get(i).chrom.getAllele().substring(0,generationCounter);
                allele12 = oldPopulation.get(i).chrom.getAllele().substring(generationCounter,ALLE_LENGTH);
                allele21 = oldPopulation.get(i+1).chrom.getAllele().substring(0,generationCounter);
                allele22 = oldPopulation.get(i+1).chrom.getAllele().substring(generationCounter,ALLE_LENGTH);
                allele11 = allele11 + allele22;
                allele21 = allele21 + allele12;
                newPopulation.add(new Individual(allele11));
                newPopulation.add(new Individual(allele21));
            } else {
                newPopulation.add(oldPopulation.get(i));
                newPopulation.add(oldPopulation.get(i+1));
            }
        }
    }
    
    public void Mutate() {
        update();
        oldPopulation = new ArrayList<Individual>(newPopulation);
        newPopulation = new ArrayList<Individual>(MAX_POP);
        for (int i = 0; i < MAX_POP; i++) {
            generationCounter = randomizer.nextInt(100000) % 100;
            if (generationCounter >= (100 - pMutation)) {
                generationCounter = randomizer.nextInt(100000) % ALLE_LENGTH;
                String alle = oldPopulation.get(i).chrom.getAllele().substring(0, generationCounter);
                int rand = (randomizer.nextInt(100000) % maxCounter) + 1;
                alle = alle + (char)(rand + 48) + oldPopulation.get(i).chrom.getAllele().substring(generationCounter + 1,ALLE_LENGTH);
                newPopulation.add(new Individual(alle));
            } else {
                newPopulation.add(oldPopulation.get(i));
            }
        }
    }
    
    //persentase untuk random
    private void countPersentase() {
        int[] keuntungan = new int[Environment.typesOfMachine.size()];
        int i = 0;
        for(Machine m : Environment.typesOfMachine) {
            if (m.dependant < 0) {
                if (keuntungan[i] == 0)
                    keuntungan[i] = m.productiv * Environment.typesOfProduct.get(i).price*m.maxTime;
            } else {
                int sisa = Environment.typesOfMachine.get(m.dependant-1).productiv - m.inputAmount;
                int depend = m.dependant;
                int awal = m.type;
                if (sisa >= 0) {
                    while (Environment.typesOfMachine.get(depend - 1).dependant > 0) {
                        keuntungan[depend - 1] = (Environment.typesOfMachine.get(awal-1).productiv * Environment.typesOfProduct.get(awal-1).price * Environment.typesOfMachine.get(awal-1).maxTime + sisa * Environment.typesOfProduct.get(depend-1).price);
                        keuntungan[awal - 1] = keuntungan[depend - 1] + (keuntungan[awal-1] / 2);
                        sisa = Environment.typesOfMachine.get(depend-1).productiv - Environment.typesOfMachine.get(awal-1).inputAmount;
                        awal = depend;
                        depend = Environment.typesOfMachine.get(depend-1).dependant;
                    }
                    keuntungan[depend - 1] = (Environment.typesOfMachine.get(awal-1).productiv * Environment.typesOfProduct.get(awal-1).price * Environment.typesOfMachine.get(awal-1).maxTime + sisa * Environment.typesOfProduct.get(depend-1).price);
                        keuntungan[awal - 1] = keuntungan[depend - 1] + (keuntungan[awal-1] / 2);
                } else {
                    sisa = Math.abs(sisa);
                    keuntungan[i] = (m.productiv * Environment.typesOfProduct.get(i).price * m.maxTime) / ((sisa/Environment.typesOfMachine.get(m.dependant-1).productiv) + 1);
                    keuntungan[m.dependant - 1] = keuntungan[m.dependant - 1] + (keuntungan[i] / 2);
                }
            }
            i++;
        }
        
        int total = 0;
        for (int j = 0; j < Environment.typesOfMachine.size(); j++) {
            total+=keuntungan[j];
        }
        
        for (int j = 0; j < Environment.typesOfMachine.size(); j++) {
            persentase.add(keuntungan[j] * 100 /total);
            //System.out.println(keuntungan[j]);
        }
    }
    
    private void countPersenSelect() {
        persenSelect.clear();
        float sum = 0;
        for (Individual i : newPopulation) {
            sum += i.getFitness();
        }
        for (Individual i : newPopulation) {
            persenSelect.add(i.getFitness() * 100 / sum);
        }
        
    }
    
    //untuk pengecekan populasi
    public void print() {
        for (int i = 0; i < newPopulation.size(); i++) {
            System.out.println("I[" + i + "]  |  " + newPopulation.get(i).chrom.getAllele() + "  |  " + newPopulation.get(i).fitness+" | ");
        }
    }
    
    public String printS() {
        String str = new String();
        for (int i = 0; i < newPopulation.size(); i++) {
            str += "I[" + i + "]  =  " + newPopulation.get(i).chrom.getAllele() + "  |  " + newPopulation.get(i).fitness + "\n";
        }
        return str;
    }
    
    //meloop kemungkinan solusi
    public void geneticAlgorithm() {
        while (isLoop) {
            System.out.println("=====================");
            System.out.println("Kondisi Awal");
            System.out.println("=====================");
            this.print();
            System.out.println("=====================");
            
            this.Select();
            System.out.println("=====================");
            System.out.println("Hasil Seleksi");
            System.out.println("=====================");
            this.print();
            System.out.println("=====================");
            
            this.CrossOver();
            System.out.println("=====================");
            System.out.println("Hasil CrossOver");
            System.out.println("=====================");
            this.print();
            System.out.println("=====================");
            
            this.Mutate();
            System.out.println("=====================");
            System.out.println("Hasil Mutasi");
            System.out.println("=====================");
            this.print();
            System.out.println("=====================");
            
            isLoop = false;      //tidak digunakan hanya untuk debug saja
        }
    }
    
    public String maxFitness() {
        float max = 0;
        String str = new String();
        for (Individual i : newPopulation) {
            if (max < i.getFitness()) {
                max = i.getFitness();
                str = i.chrom.getAllele();
            }
        }
        
        return str;
    }
    
    public float getMaxFitness() {
        float max = 0;
        for (Individual i : newPopulation) {
            if (max < i.getFitness()) {
                max = i.getFitness();
            }
        }
        
        return max;
    }
    
    private boolean allZero() {
        boolean isThereFitness = false;
        int is = 0;
        for (Individual i : newPopulation) {
            if (i.getFitness() > 0) {
                is++;
            }
        }
        
        if (is >= 3) {
            isThereFitness = true;
        }
        return !isThereFitness;
    }
    
    private void update() {
        if (maxFitness <= getMaxFitness()) {
            maxFitness = getMaxFitness();
            maxAllele = maxFitness();
            //System.out.println(maxFitness);
        }
    }
}
